using System;
using System.Collections.Generic;
using System.Xml;
using SchemaExplorer;

/// <summary>
/// Summary description for Class1
/// </summary>
public class BaseTemplate : CodeSmith.Engine.CodeTemplate
{
    public BaseTemplate()
	{
		//
		// TODO: Add constructor logic here
		//
	}

    protected void LoadFile(string path, SchemaExplorer.DatabaseSchema db)
    {
        System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
        XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
        nsmgr.AddNamespace("ns", "http://www.w3schools.com");

        doc.Load(path);

		
        string namePath = "/class/name";
        string preNamespacePath = "/class/namespace/preProject";
        string postNamespacePath = "/class/namespace/postProject";
		string propertiesPath = "/class/properties/property";
        string putPath = "/class/put";
        string deletePath = "/class/delete";
        string getAllPath = "/class/getAll/procedure";
        string getItemPath = "/class/getItem/procedure";

        name = GetNodeValue(doc.DocumentElement, namePath, nsmgr);
        preNamespace = GetNodeValue(doc.DocumentElement, preNamespacePath, nsmgr);
        postNamespace = GetNodeValue(doc.DocumentElement, postNamespacePath, nsmgr);
        XmlNodeList propertiesNodes = GetNodes(doc.DocumentElement, propertiesPath, nsmgr);
		
		_properties = new List<Property>();
		foreach(XmlNode n in propertiesNodes)
		{
            string tmpName = GetNodeValue(n, "@name", nsmgr);
            string tmpType = GetNodeValue(n, "@type", nsmgr);
            string tmpAccess = GetNodeValue(n, "@access", nsmgr);
            bool tmpNullable = bool.Parse(GetNodeValue(n, "@nullable", nsmgr));
            bool tmpReadOnly = bool.Parse(GetNodeValue(n, "@readonly", nsmgr));
			_properties.Add(new Property(tmpName, tmpType, tmpAccess, tmpNullable, tmpReadOnly));
			
		}

        XmlNode xmlPutProcedure = GetNode(doc.DocumentElement, putPath, nsmgr);
        putProcedure = GetProcedure(xmlPutProcedure, nsmgr);

        XmlNode xmlDeleteProcedure = GetNode(doc.DocumentElement, deletePath, nsmgr);
        deleteProcedure = GetProcedure(xmlDeleteProcedure, nsmgr);

        XmlNodeList xmlGetAllProcedures = GetNodes(doc.DocumentElement, getAllPath, nsmgr);
        getAllProcedures = GetProcedures(xmlGetAllProcedures, nsmgr);
        XmlNodeList xmlGetItemProcedures = GetNodes(doc.DocumentElement, getItemPath, nsmgr);
        getItemProcedures = GetProcedures(xmlGetItemProcedures, nsmgr);
    }

    private string GetNodeValue(System.Xml.XmlNode root, string namePath, XmlNamespaceManager nsmgr)
    {
        System.Xml.XmlNode node = root.SelectSingleNode(namePath, nsmgr);
        if (node != null)
        {
            return node.InnerText;
        }
        else
        {
			throw new Exception(string.Format("'{0}' didnt return a node from xml '{1}'", namePath, (namePath.Contains("@") ? root.OuterXml : root.InnerXml)));
        }
    }

    private XmlNodeList GetNodes(XmlNode root, string namePath, XmlNamespaceManager nsmgr)
	{
		return root.SelectNodes(namePath, nsmgr);
	}

    private XmlNode GetNode(XmlNode root, string namePath, XmlNamespaceManager nsmgr)
    {
        return root.SelectSingleNode(namePath, nsmgr);
    }

    private Procedure GetProcedure(XmlNode root, XmlNamespaceManager nsmgr)
    {
        if (root != null)
        {
            string name = GetNodeValue(root, "name", nsmgr);
            List<Parameter> parameters = new List<Parameter>();
            XmlNodeList xmlParameters = GetNodes(root, "parameters/parameter", nsmgr);
            foreach (XmlNode node in xmlParameters)
            {
                string paramName = GetNodeValue(node, "@name", nsmgr);
                string type = GetNodeValue(node, "@type", nsmgr);
                string direction = GetNodeValue(node, "@direction", nsmgr);
                bool nullable = bool.Parse(GetNodeValue(node, "@nullable", nsmgr));
                parameters.Add(new Parameter(paramName, type, direction, nullable));
            }
            return new Procedure(name, parameters.ToArray());
        }
		else
		{
			return null;
		}
    }

    private Procedure[] GetProcedures(XmlNodeList nodes, XmlNamespaceManager nsmgr)
    {
        if (nodes != null)
        {
            List<Procedure> tmp = new List<Procedure>();
            foreach (XmlNode node in nodes)
            {
                tmp.Add(GetProcedure(node, nsmgr));
            }
            return tmp.ToArray();
        }
        else
        {
            return null;
        }
    }

    private string name;
    protected string Name
    {
        get { return name; }
    }

    private string preNamespace;
    protected string PreNamespace
    {
        get { return preNamespace; }
    }

    private string postNamespace;
    protected string PostNamespace
    {
        get { return postNamespace; }
    }

	private List<Property> _properties;
	public Property[] Properties
	{
		get
		{
			return _properties.ToArray();
		}
	}

    private Procedure deleteProcedure;
    public Procedure DeleteProcedure
	{
        get { return deleteProcedure; }	
	}

    private Procedure putProcedure;
    public Procedure PutProcedure
	{
        get { return putProcedure; }	
	}

    private Procedure[] getAllProcedures;
    public Procedure[] GetAllProcedures
	{
		get
		{
            return getAllProcedures;
		}	
	}

    private Procedure[] getItemProcedures;
    public Procedure[] GetItemProcedures
    {
        get
        {
            return getItemProcedures;
        }
    }
}

public class Property
{
	internal Property(string name, string type, string access, bool nullable, bool readOnly)
	{
		this.name = name;
		this.type = type;
		this.access = access;
		this.nullable = nullable;
		this.readOnly = readOnly;
	}
	
	private string name;
	public string Name
	{
		get{return name;}
	}
	
	private string type;
	public string PropType
	{
		get{return type;}
	}

	private string access;
	public string Access
	{
		get{return access;}
	}

	private bool nullable;
	public bool Nullable
	{
		get{return nullable;}
	}

	private bool readOnly;
	public bool ReadOnly
	{
		get{return readOnly;}
	}
}

public class Procedure
{
    public Procedure(string name, Parameter[] parameters)
    {
        this.name = name;
        this.parameters = parameters;
    }

    private string name;
    public string Name
    {
        get { return name; }
    }

    private Parameter[] parameters;
    public Parameter[] Parameters
    {
        get { return parameters; }
    }

    public Parameter[] InputParameters
    {
        get
        {
            List<Parameter> tmp = new List<Parameter>();
            foreach (Parameter p in Parameters)
            {
                if (p.Direction == "input")
                {
                    tmp.Add(p);
                }
            }
            return tmp.ToArray();
        }
    }

    public Parameter[] OutputParameters
    {
        get
        {
            List<Parameter> tmp = new List<Parameter>();
            foreach (Parameter p in Parameters)
            {
                if (p.Direction == "output")
                {
                    tmp.Add(p);
                }
            }
            return tmp.ToArray();
        }
    }
}

public class Parameter
{
    public Parameter(string name, string type, string direction, bool nullable)
    {
        this.name = name;
        this.type = type;
        this.direction = direction;
        this.nullable = nullable;
    }

    private string name;
    public string Name
    {
        get { return name; }
    }

    private string type;
    public string Type
    {
        get { return type; }
    }

    private string direction;
    public string Direction
    {
        get { return direction; }
    }

    private bool nullable;
    public bool Nullable
    {
        get { return nullable; }
    }
}